// Copyright (c) 2018, the Dart project authors.  Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:json_annotation/json_annotation.dart';

part 'input_set.g.dart';

/// A filter on files inputs or sources.
///
/// Takes a list of strings in glob format for [include] and [exclude]. Matches
/// the `glob()` function in skylark.
@JsonSerializable()
class InputSet {
  static const anything = InputSet();

  /// The globs to include in the set.
  ///
  /// May be null or empty which means every possible path (like `'**'`).
  final List<String>? include;

  /// The globs as a subset of [include] to remove from the set.
  ///
  /// May be null or empty which means every path in [include].
  final List<String>? exclude;

  const InputSet({this.include, this.exclude});

  factory InputSet.fromJson(dynamic json) {
    if (json is List) {
      if (json.any((e) => e == null)) {
        throw ArgumentError.value(
          json,
          'generate_for',
          'Include globs must not be empty',
        );
      }
      json = <String, List>{'include': json};
    } else if (json is! Map) {
      throw ArgumentError.value(
        json,
        'sources',
        'Expected a Map or a List but got a ${json.runtimeType}',
      );
    }
    final parsed = _$InputSetFromJson(json as Map);
    if (parsed.include != null && parsed.include!.any((s) => s.isEmpty)) {
      throw ArgumentError.value(
        parsed.include,
        'include',
        'Include globs must not be empty',
      );
    }
    if (parsed.exclude != null && parsed.exclude!.any((s) => s.isEmpty)) {
      throw ArgumentError.value(
        parsed.exclude,
        'exclude',
        'Exclude globs must not be empty',
      );
    }
    return parsed;
  }

  @override
  String toString() {
    final result = StringBuffer();
    if (include == null || include!.isEmpty) {
      result.write('any path');
    } else {
      result.write('paths matching $include');
    }
    if (exclude != null && exclude!.isNotEmpty) {
      result.write(' except $exclude');
    }
    return '$result';
  }
}
